package net.gdface.cli;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
 * 应用程序配置参数抽象类
 * @author guyadong
 *
 */
public abstract class BaseAppConfig extends AbstractConfiguration 
	implements CommonCliConstants {
	protected static final Logger logger = Logger.getLogger(BaseAppConfig.class.getSimpleName());

	/**
	 * 定义命令行参数
	 */
	protected final Options options = new Options();
	/**
	 * 定义命令行参数的默认值
	 */
	protected final Context defaultValue = Context.builder().build();

	private boolean trace;
	protected BaseAppConfig() {
		this(false);
	}
	/**
	 * 构造方法<br>
	 * @param withTrace 是否使用 trace 选项，为{@code true}时会将'{@code -X --trace}'自动添加到命令行参数定义中
	 */
	protected BaseAppConfig(boolean withTrace) {
		super(withTrace);
	}
	@Override
	protected Map<String, Object> getDefaultValueMap() {
		return defaultValue.getContext();
	}

	private void showError(Throwable e){
		if(isTrace()){
			e.printStackTrace();
		}else{
			logger.warning(String.format("%s:%s",e.getClass().getSimpleName(),e.getMessage()));
		}	
	}
	/**
	 * 解析命令行参数
	 * @param args
	 * @return 当前对象
	 */
	public BaseAppConfig parseCommandLine(String[] args) {
		HelpFormatter formatter = new HelpFormatter();
		CommandLineParser parser = new CmdParser();
		CommandLine cl = null;
		Options opts = getOptions();
		opts.addOption(HELP_OPTION, HELP_OPTION_LONG, false, HELP_OPTION_DESC);
		if(withTrace){
			opts.addOption(TRACE_OPTION, TRACE_OPTION_LONG, false, TRACE_OPTION_DESC);
		}
		boolean exit = false;
		try {
			// 处理Options和参数
			cl = parser.parse(opts, args);
			trace = cl.hasOption(TRACE_OPTION_LONG);
			if (!cl.hasOption(HELP_OPTION)) {
				if (cl.hasOption(DEFINE_OPTION)) {
					setSystemProperty(cl.getOptionValues(DEFINE_OPTION));
				}
				loadConfig(opts, cl);
			} else{
				exit = true;
			}
		}catch (RuntimeException e) {
			if(e.getCause() instanceof InvocationTargetException){
				Throwable targetException = ((InvocationTargetException)e.getCause()).getTargetException();
				showError(targetException);
			}else{
				showError(e);
			}
			exit = true;
		}catch (Exception e) {
			showError(e);	
			exit = true;
		}
		if (exit) {
			 // 如果发生异常，则打印出帮助信息
			formatter.printHelp(getCmdLineSyntax(), getHeader(),getOptions(),getFooter());
			System.exit(1);
		}
		doAfterParse();
		return this;
	}
	private void setSystemProperty(String[] properties) {
		if(properties.length %2 != 0){
			throw new IllegalArgumentException("INVALID properties length");
		}
		for (int i = 0; i < properties.length; i += 2) {
			System.setProperty(properties[i], properties[i + 1]);
			logger.info(String.format("set property [%s]=[%s]", properties[i], properties[i + 1]));
		}
	}
	protected String getCmdLineSyntax() {
		return String.format("%s [options]", getAppName());
	}

	public Options getOptions() {
		return options;
	}
	protected String getAppName(){
		return "Appname";
	}
	/**
	 * @return header string
	 * @see HelpFormatter#printHelp(String, String, Options, String)
	 */
	protected String getHeader() {
		return null;
	}
	/**
	 * @return footer string
	 * @see HelpFormatter#printHelp(String, String, Options, String)
	 */
	protected String getFooter() {
		return null;
		
	}

	/**
	 * {@link #parseCommandLine(String[])}结束时调用，
	 * 子类可以重写此方法，定义{@link #parseCommandLine(String[])}结束时的动作
	 */
	protected void doAfterParse() {
	}
	/**
	 * 子类重写此方法,返回常量定义
	 * @return 常量定义值对
	 */
	protected Map<String, Object> doGetConstants() {
		return Collections.emptyMap();
	}
	/**
	 * 返回指定名字的常量定义
	 * @param name 常量名
	 * @return name定义的常量值，未定义则返回{@code null}
	 */
	@SuppressWarnings("unchecked")
	public final <T>T getConstant(String name) {
		return name == null ? null : (T) doGetConstants().get(name);
	}
	/**
	 * @return 发生异常时是否输出详细堆栈信息
	 */
	public boolean isTrace() {
		return trace;
	}
	/**
	 * 将当前对象转为type指定的类型返回,用于链式调用返回子类对象<br>
	 * @param type 当前对象的类型
	 * @return 当前类的子类对象
	 */
	@SuppressWarnings("unchecked")
	public final <T extends BaseAppConfig> T self(Class<T>type){
		return (T) this;
	}
	/**
	 * 重写parse方法，当命令行包含{@code -h}选项时不抛出异常
	 * @author guyadong
	 *
	 */
	private class CmdParser extends DefaultParser {
		@Override
		public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
				throws ParseException {
			try {
				return super.parse(options, arguments, properties, stopAtNonOption);
			} catch (ParseException e) {
				if(cmd.hasOption(HELP_OPTION)){
					return cmd;
				}
				throw e;
			}
		}
	}
}
